#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
新闻股票趋势分析系统主程序
每日实时分析新闻并给出投资策略
"""

import os
import sys
import json
import time
import logging
import argparse
import traceback
import datetime
from datetime import datetime
import schedule
import threading
import yaml  # For loading YAML configs
from pathlib import Path

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('logs/app.log', encoding='utf-8')
    ]
)
logger = logging.getLogger(__name__)

# 确保所需目录存在
os.makedirs('logs', exist_ok=True)

# 添加当前目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

# 导入各个模块
try:
    from src.collectors.news_collector import NewsCollector
    from src.processors.text_processor import TextProcessor
    from src.predictors.trend_predictor import TrendPredictor
    from src.analyzers.strategy_generator import StrategyGenerator
    from src.notifiers.notification_service import NotificationService
    from src.utils.config_loader import ConfigLoader
except ImportError as e:
    logger.error(f"导入模块失败: {e}")
    sys.exit(1)

# 全局变量 - Flask应用
# 在实例方法中不要直接使用这些变量，而是使用self属性
flask_app = None
flask_news_updater = None

# 尝试导入Web模块
try:
    from src.web.web_analyzer import app as flask_app, news_updater as flask_news_updater
    logger.info("成功从src.web导入web_analyzer模块")
except ImportError:
    logger.warning("无法从src.web导入web_analyzer模块，将尝试使用本地web_analyzer.py")
    try:
        from web_analyzer import app as flask_app, news_updater as flask_news_updater
        logger.info("成功从根目录导入web_analyzer模块")
    except ImportError as e:
        logger.warning(f"无法导入web_analyzer模块: {e}")

class StockNewsAnalysisSystem:
    """新闻股票趋势分析系统，协调各个模块的工作"""
    
    def __init__(self, config_path='config/config.yaml', mode='all'):
        """
        初始化系统
        
        Args:
            config_path: 配置文件路径
            mode: 运行模式，可选值: all, web, once, schedule, interactive, test
        """
        # 确保日志目录存在
        Path('logs').mkdir(exist_ok=True)
        
        # 加载配置
        self.config_path = config_path
        self.config = self._load_config(config_path)
        
        # 记录运行模式
        self.mode = mode
        
        # 根据运行模式选择性初始化组件
        if mode != 'web':
            # 仅在非web模式下初始化全部组件
            try:
                # 初始化各个模块
                self.news_collector = NewsCollector(config_path)
                self.text_processor = TextProcessor(config_path)
                self.trend_predictor = TrendPredictor(config_path)
                self.strategy_generator = StrategyGenerator(config_path)
                self.notification_service = NotificationService(config_path)
                
                # 获取调度配置
                self.scheduler_config = self.config['system']['scheduler']
                
                logger.info("系统初始化完成")
                
                # 启动通知
                if self.config.get('notification', {}).get('settings', {}).get('notify_on_startup', False):
                    self._send_notification("系统启动通知", "股票新闻分析系统已启动，准备开始分析。")
            except Exception as e:
                logger.warning(f"初始化组件时发生错误: {e}")
                if mode != 'web':  # 仅在非web模式下抛出异常
                    raise
        
        # 使用来自web_analyzer.py的Flask应用
        global flask_app
        self.web_app = flask_app
        
        # 记录日志
        if self.web_app:
            logger.info("Web应用初始化完成")
        else:
            logger.warning("Web应用初始化失败，flask_app为None")
    
    def _load_config(self, config_path):
        """加载配置文件"""
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                if config_path.endswith('.yaml') or config_path.endswith('.yml'):
                    config = yaml.safe_load(f)
                else:
                    config = json.load(f)
            logger.info(f"成功加载配置文件: {config_path}")
            return config
        except Exception as e:
            logger.error(f"加载配置文件失败: {e}")
            return None
    
    def _send_notification(self, title, content, attachment=None):
        """
        发送通知
        
        Args:
            title: 通知标题
            content: 通知内容
            attachment: 附件路径（可选）
        """
        try:
            if self.notification_service:
                self.notification_service.send_notification(title, content, attachment)
            else:
                logger.warning("通知服务未初始化，无法发送通知")
        except Exception as e:
            logger.error(f"发送通知失败: {e}")
            logger.debug(traceback.format_exc())
    
    def run_once(self):
        """执行一次完整的分析流程"""
        try:
            logger.info("开始执行一次完整的分析流程")
            
            # 收集新闻
            if self.mode != 'web':  # 在Web模式下，可能只需要启动Web服务而不执行分析
                logger.info("开始收集新闻")
                news_file_path = self.news_collector.collect_news()
                
                if not news_file_path:
                    logger.warning("没有收集到新的新闻数据，分析流程终止")
                    return
                
                logger.info(f"新闻收集完成，保存路径: {news_file_path}")
                
                # 处理新闻文本
                logger.info("开始处理新闻文本")
                processed_news = self.text_processor.process_news_file(news_file_path)
                
                # 预测股票趋势
                logger.info("开始预测股票趋势")
                analysis_results = self.trend_predictor.analyze_all_stocks(processed_news)
                
                # 生成投资策略
                logger.info("开始生成投资策略")
                investment_advice, strategy_report, report_path = self.strategy_generator.generate_strategy(analysis_results)
                
                # 打印策略报告摘要
                self._print_report_summary(strategy_report)
                
                # 如果配置了通知，发送通知
                notification_config = self.config.get('notification', {})
                if notification_config.get('enabled', False):
                    logger.info("发送分析结果通知")
                    self._send_notification(
                        "股票分析完成",
                        f"分析已完成，推荐关注 {len(strategy_report.get('recommended_stocks', []))} 只股票。\n"
                        f"完整报告路径: {report_path}",
                        attachment=report_path if os.path.exists(report_path) else None
                    )
                
                logger.info("完整分析流程执行完毕")
                return report_path
            else:
                logger.info("Web模式下跳过分析流程")
                return None
                
        except Exception as e:
            logger.error(f"执行分析流程失败: {e}")
            logger.debug(traceback.format_exc())
            return None
    
    def _print_report_summary(self, strategy_report):
        """打印策略报告摘要"""
        if not strategy_report:
            logger.warning("策略报告为空")
            return
            
        logger.info("=" * 50)
        logger.info("投资策略报告摘要")
        logger.info("=" * 50)
        
        # 打印推荐股票
        if 'recommended_stocks' in strategy_report:
            logger.info("推荐关注的股票:")
            for stock in strategy_report['recommended_stocks'][:5]:  # 只打印前5只
                logger.info(f"  - {stock['name']} ({stock['code']}): {stock['reason']}")
            
            if len(strategy_report['recommended_stocks']) > 5:
                logger.info(f"  ... 共 {len(strategy_report['recommended_stocks'])} 只推荐股票")
                
        # 打印风险提示
        if 'risk_warnings' in strategy_report and strategy_report['risk_warnings']:
            logger.info("\n风险提示:")
            for warning in strategy_report['risk_warnings']:
                logger.info(f"  - {warning}")
                
        # 打印市场趋势
        if 'market_trend' in strategy_report:
            logger.info(f"\n市场整体趋势: {strategy_report['market_trend']['description']}")
            
        logger.info("=" * 50)
        logger.info(f"完整报告路径: {strategy_report.get('report_path', '未生成')}")
        logger.info("=" * 50)
    
    def run_scheduled(self):
        """设置定时任务定期运行分析流程"""
        try:
            logger.info("开始设置定时任务")
            
            # 获取配置的定时任务时间
            schedule_config = self.config.get('system', {}).get('schedule', {})
            collection_time = schedule_config.get('collection_time', '08:00')
            analysis_time = schedule_config.get('analysis_time', '09:00')
            
            logger.info(f"设置新闻收集时间: {collection_time}")
            logger.info(f"设置分析运行时间: {analysis_time}")
            
            # 设置定时任务
            schedule.every().day.at(collection_time).do(self.news_collector.collect_news)
            schedule.every().day.at(analysis_time).do(self.run_once)
            
            logger.info("定时任务设置完成，开始运行")
            
            # 启动定时任务
            while True:
                schedule.run_pending()
                time.sleep(60)  # 每分钟检查一次是否有定时任务需要执行
                
        except Exception as e:
            logger.error(f"定时任务设置失败: {e}")
            logger.debug(traceback.format_exc())
            

    def start_web_server(self):
        """启动Web服务器 (为了向后兼容)"""
        logger.info("调用start_web_server，将重定向到run_web_app")
        return self.run_web_app()

    def run_all(self):
        """启动所有服务"""
        try:
            # 启动Web服务器（在单独的线程中）
            if self.web_app and self.config.get('web', {}).get('enabled', True):
                web_thread = threading.Thread(target=self.run_web_app)
                web_thread.daemon = True
                web_thread.start()
                logger.info("Web服务器已在后台启动")
            
            # 启动定时任务
            logger.info("启动定时任务")
            self.run_scheduled()
            
        except KeyboardInterrupt:
            logger.info("收到中断信号，系统正在关闭...")
        except Exception as e:
            logger.error(f"启动服务失败: {e}")

    def run_interactive(self):
        """交互式运行系统"""
        try:
            while True:
                print("\n" + "=" * 50)
                print("新闻股票趋势分析系统")
                print("=" * 50)
                print("1. 执行完整分析流程")
                print("2. 仅收集新闻")
                print("3. 仅处理新闻")
                print("4. 仅预测趋势")
                print("5. 仅生成策略")
                print("6. 启动定时任务")
                print("0. 退出")
                print("-" * 50)
                
                choice = input("请选择操作: ")
                
                if choice == '1':
                    self.run_once()
                
                elif choice == '2':
                    news_list, news_file = self.news_collector.collect_all()
                    if news_file:
                        print(f"成功收集并保存了 {len(news_list)} 条新闻到 {news_file}")
                    else:
                        print("新闻收集失败")
                
                elif choice == '3':
                    news_file = input("请输入新闻文件路径: ")
                    if os.path.exists(news_file):
                        news_list = self.text_processor.load_news(news_file)
                        processed_news = self.text_processor.process_news(news_list)
                        processed_news_file = self.text_processor.save_processed_news(processed_news)
                        if processed_news_file:
                            print(f"成功处理并保存了 {len(processed_news)} 条新闻到 {processed_news_file}")
                        else:
                            print("新闻处理失败")
                    else:
                        print(f"文件不存在: {news_file}")
                
                elif choice == '4':
                    processed_news_file = input("请输入处理后的新闻文件路径: ")
                    if os.path.exists(processed_news_file):
                        processed_news = self.trend_predictor.load_processed_news(processed_news_file)
                        analysis_results = self.trend_predictor.analyze_all_stocks(processed_news)
                        analysis_file = self.trend_predictor.save_analysis_results(analysis_results)
                        if analysis_file:
                            print(f"成功分析 {len(analysis_results)} 只股票并保存结果到 {analysis_file}")
                        else:
                            print("趋势预测失败")
                    else:
                        print(f"文件不存在: {processed_news_file}")
                
                elif choice == '5':
                    analysis_file = input("请输入分析结果文件路径: ")
                    if os.path.exists(analysis_file):
                        investment_advice, strategy_report, report_path = self.strategy_generator.generate_strategy(analysis_file)
                        if report_path:
                            print(f"成功生成投资策略并保存到 {report_path}")
                            self._print_report_summary(strategy_report)
                        else:
                            print("策略生成失败")
                    else:
                        print(f"文件不存在: {analysis_file}")
                
                elif choice == '6':
                    print("启动定时任务，按Ctrl+C退出...")
                    self.run_scheduled()
                
                elif choice == '0':
                    print("谢谢使用，再见！")
                    break
                
                else:
                    print("无效的选择，请重新输入")
        
        except KeyboardInterrupt:
            print("\n程序已终止")
        except Exception as e:
            logger.error(f"交互式运行失败: {e}")

    def run_test(self, test_file=None):
        """
        使用测试数据运行分析流程
        
        Args:
            test_file: 测试数据文件路径，如果为None则使用默认测试数据
        """
        try:
            logger.info("使用测试数据运行分析流程")
            
            # 设置测试数据文件
            if test_file is None:
                test_file = 'data/news/test_news.json'
                logger.info(f"未指定测试数据文件，使用默认测试数据: {test_file}")
            
            # 检查测试数据文件是否存在
            if not os.path.exists(test_file):
                # 如果测试数据文件不存在，创建一个简单的测试数据
                logger.warning(f"测试数据文件 {test_file} 不存在，创建简单测试数据")
                
                # 确保目录存在
                os.makedirs(os.path.dirname(test_file), exist_ok=True)
                
                # 创建简单测试数据
                test_data = {
                    "news_items": [
                        {
                            "title": "测试新闻1：科技股持续上涨",
                            "content": "今日科技板块多数个股上涨，其中阿里巴巴涨幅超过5%，腾讯、百度等科技巨头也有不错表现。",
                            "source": "测试财经网",
                            "url": "https://example.com/news/1",
                            "publish_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        },
                        {
                            "title": "测试新闻2：医药行业景气度提升",
                            "content": "分析师指出，随着医改政策进一步落实，医药行业景气度有望提升，建议关注创新药企业。",
                            "source": "测试财经日报",
                            "url": "https://example.com/news/2",
                            "publish_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        }
                    ],
                    "collection_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                }
                
                # 保存测试数据
                with open(test_file, 'w', encoding='utf-8') as f:
                    json.dump(test_data, f, ensure_ascii=False, indent=2)
                
                logger.info(f"已创建简单测试数据: {test_file}")
            
            # 加载测试数据
            print(f"加载测试数据文件: {test_file}")
            with open(test_file, 'r', encoding='utf-8') as f:
                test_data = json.load(f)
            
            print(f"测试数据类型: {type(test_data)}")
            print(f"测试数据内容: {test_data}")
            
            # 使用测试数据运行分析流程
            # 处理两种可能的格式：
            # 1. 直接是新闻列表
            # 2. 是包含"news_items"键的字典
            if isinstance(test_data, list):
                news_list = test_data  # 直接使用列表
            else:
                # 尝试获取news_items字段
                news_list = test_data.get("news_items", [])
            
            print(f"解析后的news_list类型: {type(news_list)}")
            print(f"news_list内容: {news_list}")
            
            if not news_list:
                logger.warning("测试数据中没有新闻项，无法进行分析")
                return
            
            logger.info(f"使用 {len(news_list)} 条测试新闻进行分析")
            
            # 处理新闻文本
            print("开始处理新闻文本")
            processed_news = self.text_processor.process_news(news_list)
            
            # 预测股票趋势
            print("开始预测股票趋势")
            analysis_results = self.trend_predictor.analyze_all_stocks(processed_news)
            
            # Debug: Print the structure of analysis_results
            print(f"预测结果: {analysis_results}")
            
            # Add debugging for the first result and its prediction
            if analysis_results:
                first_stock = list(analysis_results.keys())[0]
                first_result = analysis_results[first_stock]
                print(f"Debug - First stock: {first_stock}")
                print(f"Debug - First result keys: {list(first_result.keys())}")
                print(f"Debug - Prediction type: {type(first_result.get('prediction'))}")
                if isinstance(first_result.get('prediction'), dict):
                    print(f"Debug - Prediction keys: {list(first_result.get('prediction').keys())}")
                    print(f"Debug - Prediction values: {first_result.get('prediction')}")
                else:
                    print(f"Debug - Prediction value: {first_result.get('prediction')}")
            
            # 生成投资策略
            print("开始生成投资策略")
            investment_advice, strategy_report, report_path = self.strategy_generator.generate_strategy(analysis_results)
            
            # 打印策略报告摘要
            self._print_report_summary(strategy_report)
            
            logger.info(f"测试分析完成，策略报告已保存到: {report_path}")
            
            return report_path
        
        except Exception as e:
            import traceback
            print(f"测试运行失败: {e}")
            print(f"错误详情: {traceback.format_exc()}")
            logger.error(f"测试运行失败: {e}")
            logger.debug(traceback.format_exc())
            return None

    def run_web_app(self):
        """启动Web应用"""
        try:
            # 获取web app实例
            web_app = None
            web_news_updater = None
            
            # 优先使用实例变量
            if hasattr(self, 'web_app') and self.web_app:
                web_app = self.web_app
                logger.info("使用已初始化的Flask应用")
            else:
                # 尝试从不同位置导入
                try:
                    from src.web.web_analyzer import app as web_app, news_updater as web_news_updater
                    logger.info("从src.web.web_analyzer导入Flask应用")
                except ImportError:
                    try:
                        from web_analyzer import app as web_app, news_updater as web_news_updater
                        logger.info("从根目录web_analyzer导入Flask应用")
                    except ImportError as e:
                        logger.error(f"无法导入web_analyzer模块: {e}")
                        return
            
            # 如果仍然无法获取web_app
            if web_app is None:
                logger.error("没有可用的Flask应用")
                return
            
            # 如果没有获取到news_updater，使用全局变量
            if web_news_updater is None:
                global flask_news_updater
                web_news_updater = flask_news_updater
                
            # 设置新闻更新器
            if hasattr(self, 'news_collector') and web_news_updater:
                web_news_updater.set_news_collector(self.news_collector)
                logger.info("已设置新闻收集器")
            
            # 设置应用程序配置
            web_app.config['CONFIG_PATH'] = self.config_path
            
            # 设置应用程序调用回调
            web_app.config['SYSTEM_INSTANCE'] = self
            
            # 获取Web服务器配置
            web_config = self.config.get('web_server', {})
            if not web_config:
                web_config = self.config.get('web', {})
            
            host = web_config.get('host', '0.0.0.0')
            port = web_config.get('port', 5000)
            debug = web_config.get('debug', False)
            
            logger.info(f"启动Web应用服务器: http://{host}:{port}")
            
            # 启动Flask应用
            web_app.run(host=host, port=port, debug=debug)
            
        except Exception as e:
            logger.error(f"启动Web应用失败: {e}")
            logger.debug(traceback.format_exc())

def parse_args():
    """
    解析命令行参数
    
    Returns:
        argparse.Namespace: 解析后的参数
    """
    parser = argparse.ArgumentParser(description='低位领涨股分析系统')
    parser.add_argument('-c', '--config', type=str, default='config/config.yaml', 
                        help='配置文件路径 (默认: config/config.yaml)')
    parser.add_argument('-m', '--mode', type=str, default='all', 
                        choices=['once', 'schedule', 'interactive', 'test', 'web', 'all'],
                        help='运行模式: once(单次), schedule(定时), interactive(交互), test(测试), web(网页), all(全部)')
    parser.add_argument('-t', '--test-file', type=str, 
                        help='测试数据文件路径 (仅在测试模式下有效)')
    return parser.parse_args()

def main():
    """主函数，程序入口点"""
    args = parse_args()
    config_file = args.config
    
    try:
        # 创建系统实例
        system = StockNewsAnalysisSystem(config_file, mode=args.mode)
        
        # 根据不同模式执行不同操作
        if args.mode == 'once':
            system.run_once()
        elif args.mode == 'schedule':
            system.run_scheduled()
        elif args.mode == 'interactive':
            system.run_interactive()
        elif args.mode == 'test':
            system.run_test(args.test_file)
        elif args.mode == 'web':
            system.run_web_app()
        elif args.mode == 'all':
            # 默认运行一次分析并启动Web应用
            system.run_once()
            system.run_web_app()
        
        return 0
        
    except Exception as e:
        logger.error(f"系统运行失败: {e}")
        logger.debug(traceback.format_exc())
        return 1

if __name__ == "__main__":
    main() 